-- Copyright 2019 Google LLC
--
-- Use of this source code is governed by a BSD-style
-- license that can be found in the LICENSE file or at
-- https://developers.google.com/open-source/licenses/bsd

module Live.Web (runWeb, generateHTML) where

import Control.Concurrent (readChan)
import Control.Monad (forever)

import Network.Wai (Application, StreamingBody, pathInfo,
                    responseStream, responseLBS, responseFile)
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Types (status200, status404)
import Data.Aeson (ToJSON, encode)
import Data.Binary.Builder (fromByteString)
import Data.ByteString.Lazy (toStrict)
import qualified Data.ByteString as BS
import System.Directory (withCurrentDirectory)

-- import Paths_dex (getDataFileName)
import RenderHtml
import Live.Eval
import TopLevel

runWeb :: FilePath -> EvalConfig -> TopStateEx -> IO ()
runWeb fname opts env = do
  resultsChan <- watchAndEvalFile fname opts env
  putStrLn "Streaming output to http://localhost:8000/"
  run 8000 $ serveResults resultsChan

pagesDir :: FilePath
pagesDir = "pages"

generateHTML :: FilePath -> FilePath -> EvalConfig -> TopStateEx -> IO ()
generateHTML sourcePath destPath cfg env = do
  finalState <- evalFileNonInteractive sourcePath cfg env
  results <- renderResults finalState
  withCurrentDirectory pagesDir do
    renderStandaloneHTML destPath results

serveResults :: EvalServer -> Application
serveResults resultsSubscribe request respond = do
  print (pathInfo request)
  case pathInfo request of
    []            -> respondWith "static/dynamic.html" "text/html"
    ["style.css"] -> respondWith "static/style.css"  "text/css"
    ["index.js"]  -> respondWith "static/index.js"   "text/javascript"
    ["getnext"]   -> respond $ responseStream status200
                       [ ("Content-Type", "text/event-stream")
                       , ("Cache-Control", "no-cache")]
                       $ resultStream resultsSubscribe
    _ -> respond $ responseLBS status404
           [("Content-Type", "text/plain")] "404 - Not Found"
  where
    respondWith dataFname ctype = do
      fname <- return dataFname -- lets us skip rebuilding during development
      -- fname <- getDataFileName dataFname
      respond $ responseFile status200 [("Content-Type", ctype)] fname Nothing

resultStream :: EvalServer -> StreamingBody
resultStream resultsServer write flush = do
  sendUpdate ("start"::String)
  (initResult, resultsChan) <- subscribeIO resultsServer
  (renderedInit, renderUpdateFun) <- renderResultsInc initResult
  sendUpdate renderedInit
  forever $ readChan resultsChan >>= renderUpdateFun >>= sendUpdate
  where
    sendUpdate :: ToJSON a => a -> IO ()
    sendUpdate x = write (fromByteString $ encodePacket x) >> flush

encodePacket :: ToJSON a => a -> BS.ByteString
encodePacket = toStrict . wrap . encode
  where wrap s = "data:" <> s <> "\n\n"
